#include "Utilities.h"
#include <cmath>
using namespace Utilities;
using namespace std;

ParameterNormalization ::ParameterNormalization(double temperature)
{
	this->temperature = temperature;
	initFactors();
}

ParameterNormalization::ParameterNormalization()
{
	this->temperature = ROOM_TEMP;
	initFactors();
}

ParameterNormalization::~ParameterNormalization(void)
{
}

void ParameterNormalization::initFactors()
{
	this->lengthFactor = sqrt(EPSILON * BOLTZMAN * this->temperature / CHARGE / CHARGE / INTRINSIC_CONC_SI);
	this->potentialFactor = BOLTZMAN * this->temperature / CHARGE;
	this->elecFieldFactor = potentialFactor / lengthFactor;
	this->concFactor = INTRINSIC_CONC_SI;
}

double ParameterNormalization::PushLength(double length)
{
	return length / lengthFactor;
}

double ParameterNormalization::PullLength(double length)
{
	return length * lengthFactor;
}

double ParameterNormalization::PushPotential(double potential)
{
	return potential / potentialFactor;
}

double ParameterNormalization::PullPotential(double potential)
{
	return potential * potentialFactor;
}

double ParameterNormalization::PushElecField(double elecField)
{
	return elecField / elecFieldFactor;
}

double ParameterNormalization::PullElecField(double elecField)
{
	return elecField * elecFieldFactor;
}

double ParameterNormalization::PushConcentration(double conc)
{
	return conc / concFactor;
}

double ParameterNormalization::PullConcentration(double conc)
{
	return conc * concFactor;
}

void ParameterNormalization::ConverseLengthVector( std::vector<double> &real, std::vector<double> &norm, ConverseDirection direction )
{
	switch (direction)
	{
	case Push:
		for (std::vector<double>::size_type ix = 0; ix != real.size(); ++ix)
		{
			norm[ix] = this->PushLength(real[ix]);
		}
		break;
	case Pull:
		for (std::vector<double>::size_type ix = 0; ix != norm.size(); ++ix)
		{
			real[ix] = this->PullLength(norm[ix]);
		}
		break;
	}
}

void ParameterNormalization::ConversePotentialVector( std::vector<double> &real, std::vector<double> &norm, ConverseDirection direction )
{
	switch (direction)
	{
	case Push:
		for (std::vector<double>::size_type ix = 0; ix != real.size(); ++ix)
		{
			norm[ix] = this->PushPotential(real[ix]);
		}
		break;
	case Pull:
		for (std::vector<double>::size_type ix = 0; ix != norm.size(); ++ix)
		{
			real[ix] = this->PullPotential(norm[ix]);
		}
		break;
	}
}

void ParameterNormalization::ConverseElecFieldVector( std::vector<double> &real, std::vector<double> &norm, ConverseDirection direction )
{
	switch (direction)
	{
	case Push:
		for (std::vector<double>::size_type ix = 0; ix != real.size(); ++ix)
		{
			norm[ix] = this->PushElecField(real[ix]);
		}
		break;
	case Pull:
		for (std::vector<double>::size_type ix = 0; ix != norm.size(); ++ix)
		{
			real[ix] = this->PullElecField(norm[ix]);
		}
		break;
	}
}

void ParameterNormalization::ConveseConcVector( std::vector<double> &real, std::vector<double> &norm, ConverseDirection direction )
{
	switch (direction)
	{
	case Push:
		for (std::vector<double>::size_type ix = 0; ix != real.size(); ++ix)
		{
			norm[ix] = this->PushConcentration(real[ix]);
		}
		break;
	case Pull:
		for (std::vector<double>::size_type ix = 0; ix != norm.size(); ++ix)
		{
			real[ix] = this->PullConcentration(norm[ix]);
		}
		break;
	}
}

void PrintConsole::PrintVector(std::vector<double>& vec)
{
	std::cout << "The vector has " << vec.size() << " items" << std::endl;
	for (std::vector<double>::size_type ix = 0; ix != vec.size(); ++ix)
	{
		std::cout << vec[ix] << std::endl;
	}
}

void PrintConsole::Error(const char* p, const char* p2 = "")
{
	std::cerr << p << ' ' << p2 << '\n';
	exit(1);
}

void TwoDimBC::SetType(std::string bcType, TwoDimBoundaryPosition thePosition)
{
	this->Position = thePosition;
	if (bcType == "First")
		this->Type = First;
	else if (bcType == "Second")
		this->Type = Second;
	else if (bcType == "Artificial")
		this->Type = Artificial;
	else
	{
		string positionString = "";
		switch (Position)
		{
		case PositionLeft:
			positionString = "left";
			break;
		case PositionRight:
			positionString = "right";
			break;
		case PositionUp:
			positionString = "upper";
			break;
		case PositionDown:
			positionString = "lower";
			break;
		}
		PrintConsole::Error("Wrong boundary condition of the", positionString.c_str());
	}
}

void TwoDimBC::SetValue(double inputValue)
{
	//the difference between second and aritificial boundary condition is that the value of the latter is 0.
	switch (this->Type)
	{
	case First:
		this->ValuePotential = inputValue;
		break;
	case Second:
		switch (this->Position)
		{
		case PositionUp:
		case PositionDown:
			this->ValueElecFieldY = inputValue;
			break;
		case PositionLeft:
		case PositionRight:
			this->ValueElecFieldX = inputValue;
			break;
		}
		break;
	case Artificial:
		switch (this->Position)
		{
		case PositionUp:
		case PositionDown:
			this->ValueElecFieldY = 0;
			break;
		case PositionLeft:
		case PositionRight:
			this->ValueElecFieldX = 0;
			break;
		}
		break;
	}
}

void PrintConsole::PrintMy2DVector(My2DVector<double>& the2DVector)
{
	std::cout << "In this 2D vector, Nx = " << the2DVector.RowSize << ", Ny = " << the2DVector.ColSize << std::endl;
	for (int y = the2DVector.ColSize-1; y >= 0; --y)
	{
		for (int x = 0; x != the2DVector.RowSize; ++x)
		{
			std::cout << the2DVector(x, y) << '\t';
		}
		std::cout << std::endl;
	}
}

void Utilities::PrintConsole::PrintVectorIn2D(std::vector<double> &theVector, int rowSize, int colSize)
{
	std::cout << "In this 2D vector, Nx = " << rowSize << ", Ny = " << colSize << std::endl;
	My2DVector<double> positioner = My2DVector<double>(rowSize, colSize);
	int index = 0;
	for (int y = colSize-1; y >= 0; --y)
	{
		for (int x = 0; x != rowSize; ++x)
		{
			index = positioner.IndexAt(x, y);
			std::cout << theVector[index] << '\t';
		}
		std::cout << std::endl;
	}
}
